// Copyright (c) 2010 Omer Goshen

#include "PhysicsComponent.h"
#include "TransformComponent.h"
#include "utils/ShapeResolver.h"

using namespace Ogre;
using namespace BtOgre;

PhysicsComponent::PhysicsComponent(btDynamicsWorld* world, GameObject* go, Real mass, btCollisionShape* shape) :
	Component("PhysicsComponent"),
	mGo(go),
	mNode(go->getProperty<SceneNode*>("scenenode")),
	mWorld(world),
	mShape(shape)
{
//	mShape->setLocalScaling(BtOgre::Convert::toBullet(mNode->getScale()));
	mShape->setMargin(0);

	BtOgre::RigidBodyState *state = new BtOgre::RigidBodyState(mNode);

	btVector3 inertia(0,0,0);
	mShape->calculateLocalInertia(mass, inertia);

	btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(mass, state, shape, inertia);

	mRigidBody = new btRigidBody(rigidBodyCI);
	mRigidBody->setActivationState(DISABLE_DEACTIVATION);

	mWorld->addRigidBody(mRigidBody);

	init();
}

PhysicsComponent::PhysicsComponent(btDynamicsWorld* world, GameObject* go) :
	Component("PhysicsComponent"),
	mGo(go),
	mNode(go->getProperty<SceneNode*>("scenenode")),
	mEntity(go->getProperty<Entity*>("entity")),
	mWorld(world)
{
	Real mass = 1.0f;

	BtOgre::RigidBodyState *state = new BtOgre::RigidBodyState(mNode);

	Ogre::Matrix4 mat = Ogre::Matrix4::IDENTITY;
//	mat.setScale(mNode->getScale());

	BtOgre::StaticMeshToShapeConverter converter(mEntity, mat);
	mShape = converter.createConvex();
	mShape->setMargin(0);
//	mShape->setLocalScaling(BtOgre::Convert::toBullet(mNode->getScale()));

	bool isStatic = (mass == 0);
	btVector3 inertia(0,0,0);

	if (!isStatic)
		mShape->calculateLocalInertia(mass, inertia);

	btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(mass, state, mShape, inertia);

	mRigidBody = new btRigidBody(rigidBodyCI);
	mRigidBody->setActivationState(DISABLE_DEACTIVATION);
	mWorld->addRigidBody(mRigidBody);

	init();
}


PhysicsComponent::PhysicsComponent(btDynamicsWorld* world, GameObject* go, Real mass) :
	Component("PhysicsComponent"),
	mGo(go),
	mNode(go->getProperty<SceneNode*>("scenenode")),
	mEntity(go->getProperty<Entity*>("entity")),
	mWorld(world)
{
	BtOgre::RigidBodyState *state = new BtOgre::RigidBodyState(mNode);

	Ogre::Matrix4 mat = Ogre::Matrix4::IDENTITY;
//	mat.setScale(mNode->getScale());

	BtOgre::StaticMeshToShapeConverter converter(mEntity, mat);
	mShape = converter.createConvex();
	mShape->setMargin(0);
//	mShape->setLocalScaling(BtOgre::Convert::toBullet(mNode->getScale()));

	bool isStatic = (mass == 0);
	btVector3 inertia(0,0,0);

	if (!isStatic)
		mShape->calculateLocalInertia(mass, inertia);

	btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(mass, state, mShape, inertia);

	mRigidBody = new btRigidBody(rigidBodyCI);
	mRigidBody->setActivationState(DISABLE_DEACTIVATION);
	mWorld->addRigidBody(mRigidBody);

	init();
}

PhysicsComponent::PhysicsComponent(ComponentContainer* c, btDynamicsWorld* world, Object params) :
	Component(c, "PhysicsComponent"),
	mWorld(world)
{
//	std::cout << params.getXmlString() << std::endl;
//	cout << *mGo << endl;
	mGo = dynamic_cast<GameObject*>(c);
	assert(mGo);

	mNode = mGo->getProperty<SceneNode*>("scenenode");
	mEntity = mGo->getProperty<Entity*>("entity");

	BtOgre::RigidBodyState *state = new BtOgre::RigidBodyState(mNode);

	const char* shape = params.getWithDef("shape", "convex");
	mShape = ShapeResolver::getShape(shape, mEntity);
	mShape->setMargin(0);
//	mShape->setLocalScaling(BtOgre::Convert::toBullet(mNode->getScale()));

	btScalar mass = params.getWithDef("mass", 1.f);
	bool isStatic = (mass == 0);
	btVector3 inertia(0,0,0);
	if (!isStatic)
		mShape->calculateLocalInertia(mass, inertia);


	btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(mass, state, mShape, inertia);
	mRigidBody = new btRigidBody(rigidBodyCI);

	if(params.hasProperty("kinematic"))
		{
//			bool kinematic = Ogre::StringConverter::parseBool(params.getWithDef("kinematic", "false"));
			bool kinematic = params.getWithDef("kinematic", false);
			if(kinematic)
				mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
		}

	mRigidBody->setActivationState(DISABLE_DEACTIVATION);
	mWorld->addRigidBody(mRigidBody);

	init();
}

PhysicsComponent::PhysicsComponent(ComponentContainer* c, const std::string& name)
	: Component(c, name)
{
	init();
}


PhysicsComponent::~PhysicsComponent()
{
	mWorld->removeRigidBody(mRigidBody);
	delete mRigidBody;
}

void PhysicsComponent::update()
{
	mShape->setLocalScaling(BtOgre::Convert::toBullet(mNode->getScale()));

	mRigidBody->clearForces();
	mRigidBody->setLinearVelocity(btVector3(0,0,0));
	mRigidBody->setAngularVelocity(btVector3(0,0,0));

	BtOgre::RigidBodyState *state = new BtOgre::RigidBodyState(mNode);
	mRigidBody->setMotionState(state);
}

void PhysicsComponent::init()
{
	assert(mGo);

	mGo->addProperty<btRigidBody*>("rigidbody", mRigidBody);

	Object trans = mGo->getProperty<Object>("transform");
	assert(!trans.isNull());
	assert(!trans.properties.empty());

	trans.getProperty<Vector3>("pos").onChanged.connect(boost::bind(&PhysicsComponent::update, this));
	trans.getProperty<Vector3>("scale").onChanged.connect(boost::bind(&PhysicsComponent::update, this));
	trans.getProperty<Quaternion>("orientation").onChanged.connect(boost::bind(&PhysicsComponent::update, this));

//	mGo->onMoved.connect(boost::bind(&PhysicsComponent::update, this));
//	mGo->onScaled.connect(boost::bind(&PhysicsComponent::update, this));
//	mGo->onRotated.connect(boost::bind(&PhysicsComponent::update, this));
}
